{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# LC3 Assembly Language\n", "\n", "More abstract, with additional powers:\n", "\n", "1. Labels\n", "2. Instruction and Register names\n", "3. Assembler Directives\n", " * **.ORIG** - location to store code/data\n", " * **.END** - end assembly process\n", " * **.FILL** - Value for this memory location\n", " * **.BLKW** - Block of Words\n", " * **.STRINGZ** - Initialize memory with ASCII values, 0-terminated\n", "4. Shorthand for using decimal and hexadecimal numbers\n", "\n", "**New**: when you write in assembly code, the memory dumps will be *disassembled* automatically for you. That is, they will be converted from bits to instructions.\n", "\n", "**BUG:** Always provide a LABEL for .BLKW\n", "\n", "## Format of assembly code\n", "\n", "This is completely up to you! Some like to line up items in columns:\n", "\n", "```gas\n", "LABEL: OPCODE OPERANDS ; COMMENTS\n", " DIRECTIVE\n", "```\n", "\n", "OPCODE and OPERANDS are mandatory. All spacing is optional, but lines are meaningful.\n", "\n", "Colon after the LABEL is optional with the Calysto LC3 Assembler. Develop your own style!\n", "\n", "## Jupyter Magics - metacommands\n", "\n", "Review, and some new ones:\n", "\n", "
\n",
    " %bp [clear | SUSPENDHEX]           - show, clear, or set breakpoints\n",
    " %cont                              - continue running\n",
    " %dis [STARTHEX [STOPHEX]]          - dump memory as program\n",
    " %dump [STARTHEX [STOPHEX]]         - list memory in hex\n",
    " %exe                               - execute the program\n",
    " %mem HEXLOCATION HEXVALUE          - set memory\n",
    " %pc HEXVALUE                       - set PC\n",
    " %reg REG HEXVALUE                  - set register REG to HEXVALUE\n",
    " %regs                              - show registers\n",
    " %reset                             - reset LC3 to start state\n",
    " %step                              - execute the next instruction, increment PC\n",
    " %labels                            - show the labels after first pass\n",
    "
\n", "\n", "## Example" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "============================================================\n", "Memory disassembled:\n", "============================================================\n", " x3050: x2207 LD R1, SIX [line: 1]\n", " x3051: x2405 LD R2, NUMBER [line: 2]\n", " x3052: x56E0 AND R3, R3, #0 [line: 3]\n", "AGAIN: x3053: x16C2 ADD R3, R3, R2 [line: 7]\n", " x3054: x127F ADD R1, R1, #-1 [line: 8]\n", " x3055: x03FD BRp AGAIN [line: 9]\n", " x3056: xF025 HALT [line: 11]\n", "NUMBER: x3057: x0000 BR SIX (or 0) [line: 13]\n", "SIX: x3058: x0006 BR x305F (or 6) [line: 14]\n", " x3059: x0000 BR x305A (or 0) [line: 15]\n", "\n", "============================================================\n", "Registers:\n", "============================================================\n", "PC: x3059\n", "N: 0 Z: 1 P: 0 \n", "R0: x0000 R1: x0000 R2: x0000 R3: x0000 \n", "R4: x0000 R5: x0000 R6: x0000 R7: x0000 \n" ] } ], "source": [ "; Example from book, Figure 7.1, page 179\n", "; Program to multiply an integer by the constant 6.\n", "; Before execution, an integer must be stored in \n", "; NUMBER.\n", "\n", " .ORIG x3050\n", " LD R1, SIX\n", " LD R2, NUMBER\n", " AND R3, R3, #0 ;; clear R3; will contain product\n", "\n", ";; Loop\n", "\n", "AGAIN: ADD R3, R3, R2\n", " ADD R1, R1, #-1\n", " BRp AGAIN\n", " \n", " HALT\n", " \n", "NUMBER: .BLKW 1\n", "SIX: .FILL x0006\n", "\n", " .END\n", " " ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "============================================================\n", "Memory disassembled:\n", "============================================================\n", " x3050: x2207 LD R1, SIX [line: 1]\n", " x3051: x2405 LD R2, NUMBER [line: 2]\n", " x3052: x56E0 AND R3, R3, #0 [line: 3]\n", "AGAIN: x3053: x16C2 ADD R3, R3, R2 [line: 7]\n", " x3054: x127F ADD R1, R1, #-1 [line: 8]\n", " x3055: x03FD BRp AGAIN [line: 9]\n", " x3056: xF025 HALT [line: 11]\n", "NUMBER: x3057: x0000 BR SIX (or 0) [line: 13]\n", "SIX: x3058: x0006 BR x305F (or 6) [line: 14]\n", " x3059: x0000 BR x305A (or 0) [line: 15]\n", "\n", "============================================================\n", "Registers:\n", "============================================================\n", "PC: x3059\n", "N: 0 Z: 1 P: 0 \n", "R0: x0000 R1: x0000 R2: x0000 R3: x0000 \n", "R4: x0000 R5: x0000 R6: x0000 R7: x0000 \n" ] } ], "source": [ "; Example from book, Figure 7.1, page 179\n", "; Program to multiply an integer by the constant 6.\n", "; Before execution, an integer must be stored in \n", "; NUMBER.\n", "\n", ".ORIG x3050\n", " LD R1, SIX\n", " LD R2, NUMBER\n", " AND R3, R3, #0 ;; clear R3; will contain product\n", "\n", ";; Loop\n", "\n", "AGAIN: ADD R3, R3, R2\n", " ADD R1, R1, #-1\n", " BRp AGAIN\n", " \n", " HALT\n", " \n", "NUMBER: .BLKW 1\n", "SIX: .FILL x0006\n", "\n", ".END\n", " " ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "============================================================\n", "Memory disassembled:\n", "============================================================\n", "NUMBER: x3057: x0001 BR x3059 (or 1) [line: 13]\n" ] } ], "source": [ "%mem x3057 x1" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "============================================================\n", "Computation completed\n", "============================================================\n", "Instructions: 22\n", "Cycles: 143 (0.000072 milliseconds)\n", "\n", "============================================================\n", "Registers:\n", "============================================================\n", "PC: x048E\n", "N: 0 Z: 1 P: 0 \n", "R0: x0000 R1: x0000 R2: x0001 R3: x0006 \n", "R4: x0000 R5: x0000 R6: x0000 R7: x3057 \n" ] } ], "source": [ "%exe" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Labels\n", "\n", "We used to have to figure out PC-offsets by hand, but now the assembler will figure that out for you.\n", "\n", "Recall, for something like:" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "============================================================\n", "Memory disassembled:\n", "============================================================\n", " x4000: x2201 LD R1, SIX [line: 1]\n", " x4001: xF025 HALT [line: 2]\n", "SIX: x4002: x0017 BR x401A (or 23) [line: 3]\n", "\n", "============================================================\n", "Registers:\n", "============================================================\n", "PC: x4003\n", "N: 0 Z: 1 P: 0 \n", "R0: x0000 R1: x0000 R2: x0000 R3: x0000 \n", "R4: x0000 R5: x0000 R6: x0000 R7: x0000 \n" ] } ], "source": [ ".ORIG x4000\n", " LD R1, SIX\n", " HALT\n", "SIX: .FILL #23\n", ".END" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You would have to compute the value for SIX in line 2 manually. No more!\n", "\n", "But how would the assembler do this?\n", "\n", "## Two-pass assembler\n", "\n", "The assembler first goes through the source code collecting labels, and their locations. During the second pass, it can substitute the used label in the operands with the **label location** minus **instruction location** - 1.\n", "\n", "```gas\n", " .ORIG x4000\n", "x4000 LD R1, SIX\n", "x4001 HALT\n", "x4002 SIX: .FILL #23\n", " .END\n", "```\n", "So, the PC-offset for SIX is x4002 - x4000 - 1 = 1. Verify that this is correct.\n", "\n", "**Warning**: you still only have 9 bits to represent the distance between label and instruction!\n", "\n", "How far away can the label be?\n", "\n", "* You can represent 512 in 9 bits: 111111111. But that is a negative number\n", "* You can represent 255 in 8 bits: 011111111.\n", "\n", "So, this is ok:" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "============================================================\n", "Memory disassembled:\n", "============================================================\n", " x4000: x22FF LD R1, SIX [line: 1]\n", " x4001: xF025 HALT [line: 2]\n", "BLOCK: x4002: x0000 BR x4003 (or 0) [line: 3]\n", " x4003: x0000 - \\0\n", " x4004: x0000 - \\0\n", " x4005: x0000 - \\0\n", " x4006: x0000 - \\0\n", " x4007: x0000 - \\0\n", " x4008: x0000 - \\0\n", " x4009: x0000 - \\0\n", " x400A: x0000 - \\0\n", " x400B: x0000 - \\0\n", " x400C: x0000 - \\0\n", " x400D: x0000 - \\0\n", " x400E: x0000 - \\0\n", " x400F: x0000 - \\0\n", " x4010: x0000 - \\0\n", " x4011: x0000 - \\0\n", " x4012: x0000 - \\0\n", " x4013: x0000 - \\0\n", " x4014: x0000 - \\0\n", " x4015: x0000 - \\0\n", " x4016: x0000 - \\0\n", " x4017: x0000 - \\0\n", " x4018: x0000 - \\0\n", " x4019: x0000 - \\0\n", " x401A: x0000 - \\0\n", " x401B: x0000 - \\0\n", " x401C: x0000 - \\0\n", " x401D: x0000 - \\0\n", " x401E: x0000 - \\0\n", " x401F: x0000 - \\0\n", " x4020: x0000 - \\0\n", " x4021: x0000 - \\0\n", " x4022: x0000 - \\0\n", " x4023: x0000 - \\0\n", " x4024: x0000 - \\0\n", " x4025: x0000 - \\0\n", " x4026: x0000 - \\0\n", " x4027: x0000 - \\0\n", " x4028: x0000 - \\0\n", " x4029: x0000 - \\0\n", " x402A: x0000 - \\0\n", " x402B: x0000 - \\0\n", " x402C: x0000 - \\0\n", " x402D: x0000 - \\0\n", " x402E: x0000 - \\0\n", " x402F: x0000 - \\0\n", " x4030: x0000 - \\0\n", " x4031: x0000 - \\0\n", " x4032: x0000 - \\0\n", " x4033: x0000 - \\0\n", " x4034: x0000 - \\0\n", " x4035: x0000 - \\0\n", " x4036: x0000 - \\0\n", " x4037: x0000 - \\0\n", " x4038: x0000 - \\0\n", " x4039: x0000 - \\0\n", " x403A: x0000 - \\0\n", " x403B: x0000 - \\0\n", " x403C: x0000 - \\0\n", " x403D: x0000 - \\0\n", " x403E: x0000 - \\0\n", " x403F: x0000 - \\0\n", " x4040: x0000 - \\0\n", " x4041: x0000 - \\0\n", " x4042: x0000 - \\0\n", " x4043: x0000 - \\0\n", " x4044: x0000 - \\0\n", " x4045: x0000 - \\0\n", " x4046: x0000 - \\0\n", " x4047: x0000 - \\0\n", " x4048: x0000 - \\0\n", " x4049: x0000 - \\0\n", " x404A: x0000 - \\0\n", " x404B: x0000 - \\0\n", " x404C: x0000 - \\0\n", " x404D: x0000 - \\0\n", " x404E: x0000 - \\0\n", " x404F: x0000 - \\0\n", " x4050: x0000 - \\0\n", " x4051: x0000 - \\0\n", " x4052: x0000 - \\0\n", " x4053: x0000 - \\0\n", " x4054: x0000 - \\0\n", " x4055: x0000 - \\0\n", " x4056: x0000 - \\0\n", " x4057: x0000 - \\0\n", " x4058: x0000 - \\0\n", " x4059: x0000 - \\0\n", " x405A: x0000 - \\0\n", " x405B: x0000 - \\0\n", " x405C: x0000 - \\0\n", " x405D: x0000 - \\0\n", " x405E: x0000 - \\0\n", " x405F: x0000 - \\0\n", " x4060: x0000 - \\0\n", " x4061: x0000 - \\0\n", " x4062: x0000 - \\0\n", " x4063: x0000 - \\0\n", "\n", "============================================================\n", "Registers:\n", "============================================================\n", "PC: x4101\n", "N: 0 Z: 1 P: 0 \n", "R0: x0000 R1: x0000 R2: x0000 R3: x0000 \n", "R4: x0000 R5: x0000 R6: x0000 R7: x0000 \n" ] } ], "source": [ ".ORIG x4000\n", " LD R1, SIX\n", " HALT\n", "BLOCK: .BLKW #254\n", "SIX: .FILL #23\n", ".END" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "============================================================\n", "Computation completed\n", "============================================================\n", "Instructions: 2\n", "Cycles: 19 (0.000010 milliseconds)\n", "\n", "============================================================\n", "Registers:\n", "============================================================\n", "PC: x048E\n", "N: 0 Z: 0 P: 1 \n", "R0: x0000 R1: x0017 R2: x0000 R3: x0000 \n", "R4: x0000 R5: x0000 R6: x0000 R7: x4002 \n" ] } ], "source": [ "%exe" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "But this is not ok:" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "x4102\n" ] } ], "source": [ ".ORIG x4000\n", " LD R1, SIX\n", " HALT\n", "BLOCK: .BLKW #255\n", "SIX: .FILL #23\n", ".END" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Lesson: labels have to be within 255 instructions of where they are used.\n", "\n", "There is a way around this limitation. What is it?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Will be able to do this:\n", "\n", "```gas\n", "SIX: .FILL DATA\n", "```\n", "\n", "**Bug**: But it doesn't work yet." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## High-Level Programming Concepts\n", "\n", "### Declaration of Variables\n", "\n", "High-level | LC3\n", "-----------| ----\n", "int a; // simple variable (uninitialized)| a .BLKW 1 ; simple variable (or .FILL 0)\n", "int b = 2014; // simple initialized variable | b .FILL #2014 ; simple initialized variable\n", "int c[10]; // array of 10 (uninitialized) | c .BLKW 10 ; array of ten ints (initialized to 0)\n", "int *d = &e; // address of e | d .FILL e ; store address of e in variable d\n", "\n", "### Assignment of values\n", "\n", "\n", "High-level | LC3\n", "-----------| ----\n", "b = a; | LD R0, a ; load from memory to a register\n", " | ST R0, b ; store from register to memory\n", "b = a + 1; | LD R0, a ; load from memory to a register\n", " | ADD R0, R0, #1 ; increment value\n", " | ST R0, b ; store from register to memory\n", "\n", "\n", "### Assignment using pointers\n", "\n", "High-level | LC3\n", "-----------| ----\n", "pa = &a; | LEA R0, a ; get the address of the variable\n", " | ST R0, pa ; store it in the pointer variable\n", "b = *pa; | LDI R0, pa ; get the value at the address stored in pa\n", " | ST R0, b ; store it in b\n", "*pa = b; | LD R0, b ; load the value of b\n", " | STI R0, pa ; store it at the address stored in pa\n", "\n", "### Comparison: if\n", "\n", "\n", "High-level | LC3\n", "-----------| ----\n", "if (a < b) { | LD R0, a ; load a\n", " // do something | LD R1, b ; load b\n", "} | NOT R1, R1 ; begin 2's complement of b\n", " | ADD R1, R1, #1 ; R1 now has -b\n", " | ADD R0, R0, R1 ; R0 = a + (-b)\n", " | ; condition code now set\n", " | BRzp SKIP ; if false, skip over code\n", " | ; code to do something (the then clause)\n", " | SKIP ; remainder of code after if\n", "\n", "\n", "### Comparison: if/else\n", "\n", "**High-level**\n", "\n", "```c\n", "if (a < b) {\n", " // do something\n", "}\n", "else {\n", " // do something else\n", "}\n", "```\n", "\n", "**LC3**\n", "\n", "```gas\n", " LD R0, a ; load a\n", " LD R1, b ; load b\n", " NOT R1, R1 ; begin 2's complement of b\n", " ADD R1, R1, #1 ; R1 now has -b\n", " ADD R0, R0, R1 ; R0 = a + (-b)\n", " ; condition code now set\n", " BRzp ELSE ; if false, skip over code\n", "\n", " ; code to do something (the then clause)\n", "\n", " BR END_ELSE ; don't execute else code\n", "\n", "ELSE ; code for else clause here\n", "\n", "END_ELSE ; remainder of code after else\n", "```" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Calysto LC3", "language": "asm", "name": "calysto_lc3" }, "language_info": { "file_extension": ".asm", "mimetype": "text/x-gas", "name": "gas" } }, "nbformat": 4, "nbformat_minor": 1 }